package com.java.shop.order.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.HashMap;

import static com.java.common.constant.MqConstant.*;

@Configuration
public class MyRabbitmqConfig {

    /* 交换机 */
    @Bean
    public Exchange createExchange(){
        return new TopicExchange(order_exchangeName,true,false);
    }

    /* 普通消息队列 */
    @Bean
    public Queue createQueue(){
        return new Queue(order_ordQueue,true,false,false);
    }

    /* 延时队列 */
    @Bean
    public Queue createDeadQueue(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("x-dead-letter-exchange",order_exchangeName);
        map.put("x-dead-letter-routing-key",order_toQueueKey);
        map.put("x-message-ttl",order_eeadQueueTTL);
        return new Queue(order_deadQueue,true,false,false,map);
    }

    /* 交换机绑定关系 */
    @Bean
    public Binding toDeadQueueBinding(){
        return new Binding(order_deadQueue, Binding.DestinationType.QUEUE,order_exchangeName,order_toDeadQueueKey,null);
    }


    /* 交换机绑定关系 */
    @Bean
    public Binding toQueueBinding(){
        return new Binding(order_ordQueue,Binding.DestinationType.QUEUE,order_exchangeName,order_toQueueKey,null);
    }

    /* 使用JSON序列化机制，进行消息转换 */
    @Bean
    public MessageConverter messageCodeFormatter(){
        return new Jackson2JsonMessageConverter();
    }

    @Bean
    public Binding toWareDeadQueueBinding(){
        return new Binding(ware_ordQueue, Binding.DestinationType.QUEUE,order_exchangeName,order_toWareOrdRoutingKey,null);
    }

    /**
     * 定制rabbitmqTemplate;
     *  生产者->(ConfirmCallback)broker->(returnCallback)queue->(ack)消费者
     *  1.服务收到消息回调
     *      1.1 spring.rabbitmq.publisher-confirms=true
     *      1.2 设置确认回调ConfirmCallback
     *  2.消息队列收到消息回调（错误回调）
     *      2.1 spring.rabbitmq.publisher-returns=true
     *          spring.rabbitmq.template.mandatory=true
     *      2.2 设置确认回调ReturnCallback
     *  3.消费端确认（保证每个消息都成功消费，才重broker中删除消息）
     *      3.1 默认是自动确认的，只要消息接受到，客户端就会自动确认，服务端就会移除，所以我们需要开启手动确认ack机制
     *          有问题：
     *              有很多消息时，一旦收到一个消息，服务器会自动确认所有消息，此时发生了宕机，造成消息丢失
     *              手动ack确认模式，只有当我们明确告诉mq，消息被签收，没有ack，消息就一直是 Unacked状态，
     *              即是此时宕机，消息会重新回到队列，不会丢失
     *      3.2 如何签收
     *              channel.basicAck(deliveryTag,false);签收
     *              channel.basicNack(deliveryTag,false,true);拒签，重新入队
     *
     * 流程：
     * 生产者->(ConfirmCallback)broker->(returnCallback)queue->(ack)消费者
     * 生产者 => 一般是生产者发送mq消息时发送的消息写入数据库并给定初始状态（给定id,主体内容，初始状态[发送，到达broker,到达queue,已消费，无需重试]，重试次数等...)
     * (ConfirmCallback)broker => 到达broker后根据id查询数据库并判断是否需要重试发送数据库（结合定时任务重试，更新初始状态）
     *
     */
//    @PostConstruct //MyRabbitmqConfig创建完成后执行这个方法
//    public void rabbitmqConfirm(){
//        //设置确认回调  生产者->(ConfirmCallback)broker
//        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
//            /**
//             * 1.知道抵达broker代理 b=true
//             * @param correlationData 当前消息的唯一关联数据（唯一id)
//             * @param b 消息是否成功到达broker
//             * @param s 失败的原因
//             */
//            @Override
//            public void confirm(CorrelationData correlationData, boolean b, String s) {
//
//            }
//        });
//
//        //消息抵达队列 broker->(returnCallback)queue
//        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
//            /**
//             * 这个是失败回调（失败了才会有消息回调）
//             * 只要消息没有投递给指定队列，就触发这个失败回调
//             * @param message   投递失败的消息详细信息
//             * @param i         回复的状态码
//             * @param s         回复的文本内容
//             * @param s1        当时这个消息发送给那个交换机
//             * @param s2        当时这个消息用的那个路由键
//             */
//            @Override
//            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
//
//
//            }
//        });
//        //queue->消费者
//    }
}
